package com.wxapp.sale.service.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.enums.SqlKeyword;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.toolkit.SimpleQuery;
import com.wxapp.common.base.util.redis.RedisUtil;
import com.wxapp.common.constant.base.IBaseService;
import com.wxapp.common.constant.constant.CatchCommonConstant;
import com.wxapp.common.constant.pojo.vm.ResultVm;
import com.wxapp.common.constant.pojo.vo.UserVo;
import com.wxapp.common.core.filter.except.ThrowMyException;
import com.wxapp.common.util.check.CheckUtil;
import com.wxapp.common.util.check.CompareUtil;
import com.wxapp.common.util.condition.ConditionUtil;
import com.wxapp.common.util.copy.CopyUtil;
import com.wxapp.sale.api.enums.listener.ListenerEnum;
import com.wxapp.sale.api.enums.order.OrderStateEnum;
import com.wxapp.sale.api.feign.CustomFeign;
import com.wxapp.sale.api.pojo.entity.GoodsDO;
import com.wxapp.sale.api.pojo.entity.OrderDO;
import com.wxapp.sale.api.pojo.entity.OrderDetailDO;
import com.wxapp.sale.api.pojo.vm.order.POrderDetailVm;
import com.wxapp.sale.api.pojo.vm.order.POrderGoodsVm;
import com.wxapp.sale.api.pojo.vm.order.POrderPageVm;
import com.wxapp.sale.api.pojo.vo.listener.ListenerVo;
import com.wxapp.sale.api.pojo.vo.order.*;
import com.wxapp.sale.service.event.OrderEvent;
import com.wxapp.sale.service.mapper.GoodsMapper;
import com.wxapp.sale.service.mapper.OrderDetailMapper;
import com.wxapp.sale.service.mapper.OrderMapper;
import com.wxapp.sale.service.service.OrderService;
import com.wxapp.sale.service.util.RandomUtil;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author lgl
 */
@Service
@RequiredArgsConstructor
public class OrderServiceImpl extends IBaseService implements OrderService {
    private final static String ADD_ORDER = CatchCommonConstant.ORDER_ADD;
    private final OrderMapper orderMapper;
    private final OrderDetailMapper orderDetailMapper;
    private final RedisUtil redisUtil;
    private final GoodsMapper goodsMapper;
    private final ApplicationEventPublisher publisher;
    @Qualifier("com.wxapp.sale.api.feign.CustomFeign")
    private final CustomFeign customFeign;

    @Override
    public ResultVm<IPage<POrderPageVm>> page(UserVo userVo, QOrderPageVo qOrderPageVo) {
        List<String> orderCode = new ArrayList<>();
        if (CheckUtil.isNotNullString(qOrderPageVo.getGoodsName()) || CheckUtil.isNotNullString(qOrderPageVo.getGoodsTypeId())) {
            var query = new LambdaQueryWrapper<GoodsDO>();
            query.select(GoodsDO::getId)
                    .like(CheckUtil.isNotNullString(qOrderPageVo.getGoodsName()), GoodsDO::getName, qOrderPageVo.getGoodsName())
                    .eq(CheckUtil.isNotNullString(qOrderPageVo.getGoodsTypeId()), GoodsDO::getTypeId, qOrderPageVo.getGoodsTypeId());
            List<String> ids = SimpleQuery.list(query, GoodsDO::getId);
            if (CheckUtil.isNullList(ids)) {
                return success(new Page<>());
            }
            var orderQuery = new LambdaQueryWrapper<OrderDetailDO>();
            orderQuery.select(OrderDetailDO::getOrderCode)
                    .in(OrderDetailDO::getGoodsId, ids);
            orderCode = SimpleQuery.list(orderQuery, OrderDetailDO::getOrderCode);
            if (CheckUtil.isNullList(orderCode)) {
                return success(new Page<>());
            }
        }
        var page = ConditionUtil.getPage(qOrderPageVo, OrderDO.class);
        Map<SFunction<OrderDO, ?>, SqlKeyword> conditions = new HashMap<>();
        var condition = ConditionUtil.getSCondition(OrderDO::new, qOrderPageVo, null, conditions, null);
        condition.eq(CheckUtil.isNotNullString(qOrderPageVo.getCode()), OrderDO::getCode, qOrderPageVo.getCode())
                .le(CheckUtil.isNotNullObject(qOrderPageVo.getEndTime()), OrderDO::getCreateTime, qOrderPageVo.getEndTime())
                .ge(CheckUtil.isNotNullObject(qOrderPageVo.getStartTime()), OrderDO::getCreateTime, qOrderPageVo.getStartTime())
                .in(CheckUtil.isNotNullList(orderCode), OrderDO::getCode, orderCode)
                .eq(CheckUtil.isNotNullObject(userVo), OrderDO::getCreateBy, userVo.getId());
        Page<OrderDO> orderPage = orderMapper.selectPage(page, condition);
        return success(CopyUtil.copyPage(orderPage, POrderPageVm::new, null));
    }

    @Override
    public ResultVm<POrderDetailVm> getOrderInfo(String code) {
        var goodQuery = new LambdaQueryWrapper<OrderDO>();
        goodQuery.eq(OrderDO::getCode, code)
                .eq(OrderDO::getStatus, true);
        var order = orderMapper.selectFirst(goodQuery);
        if (CheckUtil.isNullObject(order)) {
            return error("没有找到订单信息");
        }
        var orderDetailQuery = new LambdaQueryWrapper<OrderDetailDO>();
        orderDetailQuery.eq(OrderDetailDO::getOrderCode, code)
                .eq(OrderDetailDO::getStatus, true);
        var orderDetails = orderDetailMapper.selectList(orderDetailQuery);
        if (CheckUtil.isNullObject(orderDetails)) {
            return error("没查到订单下商品信息");
        }
        Map<String, Integer> goodsNumMap = orderDetails.stream().collect(Collectors.toMap(OrderDetailDO::getGoodsId, OrderDetailDO::getBuyNum));
        var goodsS = goodsMapper.selectBatchIds(goodsNumMap.keySet());
        var orderGoodsVms = CopyUtil.copyListNew(goodsS, POrderGoodsVm::new, (s, r) -> {
            r.setBuyNum(goodsNumMap.get(s.getId()));
            return null;
        });
        var address = customFeign.getDetail(order.getAddressId());
        var pOrderDetailVm = CopyUtil.copyObjectNew(order, POrderDetailVm::new, (s, r) -> {
            r.setGoods(orderGoodsVms);
            r.setAddress(address.getData());
            return null;
        });
        return success(pOrderDetailVm);
    }

    @Override
    @GlobalTransactional(rollbackFor = {Exception.class, RuntimeException.class})
    public ResultVm<String> add(QOrderAddVo add, UserVo userVo) {
        List<String> goodIds = add.getGoods().stream().map(QBaseVo::getGoodsId).toList();
        var msg = verifyData(goodIds, userVo);
        if (CheckUtil.isNotNullString(msg)) {
            return error(msg);
        }
        var order = CopyUtil.copyObjectNew(add, OrderDO::new, (s, r) -> {
            r.setCode(RandomUtil.getOrderCode(s.getType()));
            r.setOrderState(OrderStateEnum.UNPAID);
            r.setCreateBy(userVo.getId());
            return null;
        });
        orderMapper.insert(order);
        var detailDOS = CopyUtil.copyListNew(add.getGoods(), OrderDetailDO::new, (s, r) -> {
            r.setOrderCode(order.getCode());
            return null;
        });
        orderDetailMapper.insertBatch(detailDOS);
        publisher.publishEvent(new OrderEvent(this, "用户", new ListenerVo(ListenerEnum.ORDER, order)));
        return success("下单成功");
    }

    @Override
    @GlobalTransactional(rollbackFor = {Exception.class, RuntimeException.class})
    public ResultVm<String> delete(List<String> codes) throws ThrowMyException {
        var order = getOrder(codes, List.of(OrderStateEnum.UNPAID, OrderStateEnum.FAIL));
        order.forEach(s -> s.setStatus(false));
        orderMapper.batchUpdate(order);
        return success("删除成功");
    }

    @Override
    @GlobalTransactional(rollbackFor = {Exception.class, RuntimeException.class})
    public ResultVm<String> changeStatus(QChangeStatusVo vo) {
        if (!OrderStateEnum.isNextState(vo.getNowState(), vo.getNextState())) {
            return error("状态流转出错");
        }
        var query = new LambdaQueryWrapper<OrderDO>();
        query.eq(OrderDO::getCode, vo.getOrderCode())
                .eq(OrderDO::getStatus, true);
        var orderDO = orderMapper.selectFirst(query);
        orderDO.setOrderState(vo.getNextState());
        if (CheckUtil.isNotNullString(vo.getPayAccount())) {
            orderDO.setPayAccount(vo.getPayAccount());
        }
        if (CheckUtil.isNotNullString(vo.getPayWay())) {
            orderDO.setPayWay(vo.getPayWay());
        }
        if (CheckUtil.isNotNullObject(vo.getPayTime())) {
            orderDO.setPayTime(vo.getPayTime());
        }
        orderMapper.updateById(orderDO);
        return success("更新状态成功");
    }

    private String verifyData(List<String> goodIds, UserVo userVo) {
        String msg = null;
        List<String> catches = (List<String>) redisUtil.get(ADD_ORDER + userVo.getId());
        if (CheckUtil.isNotNullObject(catches) && catches.stream().anyMatch(st -> goodIds.contains(goodIds))) {
            msg = "下单太过频繁，请稍后再试！";
        } else {
            redisUtil.set(ADD_ORDER + userVo.getId(), goodIds, 60);
        }
        var goodQuery = new LambdaQueryWrapper<GoodsDO>();
        goodQuery.in(GoodsDO::getId, goodIds)
                .eq(GoodsDO::getStatus, true)
                .eq(GoodsDO::getOnSale, true);
        var goodsNum = goodsMapper.selectCount(goodQuery);
        if (!CompareUtil.equalLong(goodsNum, (long) goodIds.size())) {
            msg = "下单商品不存在，请刷新后重新下单";
        }
        return msg;
    }

    private List<OrderDO> getOrder(List<String> code, List<OrderStateEnum> orderState, String... id) throws ThrowMyException {
        var goodQuery = new LambdaQueryWrapper<OrderDO>();
        goodQuery.in(CheckUtil.isNotNullArray(id), OrderDO::getId, id)
                .in(CheckUtil.isNotNullList(code), OrderDO::getCode, code)
                .in(OrderDO::getOrderState, orderState)
                .eq(OrderDO::getStatus, true);
        var orders = orderMapper.selectList(goodQuery);
        if (CheckUtil.isNotNullArray(orders)) {
            throw new ThrowMyException("订单不存在");
        }
        return orders;
    }
}
